package com.itheima.stock.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.itheima.stock.entity.*;
import com.itheima.stock.mapper.*;
import com.itheima.stock.properties.MarketIndexProperties;
import com.itheima.stock.response.PageResult;
import com.itheima.stock.service.StockService;
import com.itheima.stock.util.DateTimeUtil;
import com.itheima.stock.vo.*;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.MathContext;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

/**
 * @author zby
 * @created 2024-06-23 16:34
 * @description 股票业务实现类
 */
@Service
public class StockServiceImpl implements StockService {

    // 股票交易信息
    private final StockRtInfoMapper stockRtInfoMapper;
    // 板块
    private final StockBlockRtInfoMapper stockBlockRtInfoMapper;
    // 大盘指数
    private final StockMarketIndexInfoMapper marketIndexInfoMapper;
    // 大盘开盘收盘记录
    private final StockMarketLogPriceMapper marketLogPriceMapper;
    // 大盘指数编码属性类
    private final MarketIndexProperties marketIndexProperties;
    // 股票业务信息
    private final StockBusinessMapper stockBusinessMapper;

    public StockServiceImpl(StockBusinessMapper stockBusinessMapper, MarketIndexProperties marketIndexProperties, StockMarketIndexInfoMapper marketIndexInfoMapper, StockMarketLogPriceMapper marketLogPriceMapper, StockBlockRtInfoMapper stockBlockRtInfoMapper, StockRtInfoMapper stockRtInfoMapper) {
        this.stockBusinessMapper = stockBusinessMapper;
        this.marketIndexProperties = marketIndexProperties;
        this.marketIndexInfoMapper = marketIndexInfoMapper;
        this.marketLogPriceMapper = marketLogPriceMapper;
        this.stockBlockRtInfoMapper = stockBlockRtInfoMapper;
        this.stockRtInfoMapper = stockRtInfoMapper;
    }

    @Override
    public StockUpDownVO<InfoVO> stockUpDown() {

        // 1. 参数校验（无）
        // 2. 核心业务处理 （业务流程图、时序图）
        LocalDateTime last = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        //TODO  mock data
        last = LocalDateTime.parse("2022-01-06 09:55:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //TODO  mock data
        List<InfoVO> voList = stockRtInfoMapper.selectStockUpDown(last);
        // 定义区间顺序：程序定义顺序、或者配置文件定义区间顺序
        List<String> ranges = Lists.newArrayList("-10~-7", "-7~-5","-5~-3","-3~0","0~3","3~5","5~7","7~10");
        List<InfoVO> newArrayList = Lists.newArrayList();
        ranges.forEach((range)->{
            voList.forEach((vo)->{
                if (range.equals(vo.getTitle())){
                    newArrayList.add(vo);
                }
            });
        });
        // 3. 数据组装
       return StockUpDownVO.<InfoVO>builder()
                .infos(newArrayList)
                .time(last.toString())
                .build();
    }

    /**
     * T日 和 T-1日
     *
     * @return
     */

    @Override
    public TradeAmtVO<OptionVO> tradeAmt() {

        // 1. mock date 模拟时间
        // T 日   开始  2021-12-31 09:30:00    结束2021-12-31 14:30:00
        // T-1 日 开始  2021-12-30 09:30:00    结束2021-12-30 14:30:00
        // T 日 (周五) 当前时间 2024-06-29 15：26 ---> 2024-06-28 15:00:00
        LocalDateTime t = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        // 计算T 日开盘时间
        LocalDateTime t_open = DateTimeUtil.getOpenDate(t);
        // 计算T 日收盘时间  （统计范围  [t_open , t_close]）
        LocalDateTime t_close = DateTimeUtil.getCloseDate(t);
        // T-1 日  周四 当前时间 2024-06-29 15：26-->2024-06-28 15:00:00  --->2024-06-27 15:00:00
        LocalDateTime t_1 = DateTimeUtil.getPreviousTradingDay(t);
        // 计算T-1 日开盘时间
        LocalDateTime t_1_open = DateTimeUtil.getOpenDate(t_1);
        // 计算T-1 日收盘时间  （统计范围  [t_1_open , t_1_close]）
        LocalDateTime t_1_close = DateTimeUtil.getCloseDate(t_1);
        // TODO mock data
        t_open = LocalDateTime.parse("2021-12-28 09:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        t_close = LocalDateTime.parse("2021-12-28 14:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        t_1_open = LocalDateTime.parse("2021-12-27 09:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        t_1_close = LocalDateTime.parse("2021-12-27 14:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //TODO  mock data
        // 2. 查询数据库 （先写SQL --> mapper 和 xml） 使用查询一次得到两个集合！【resultMap】
        List<String> ids = marketIndexProperties.getInner();
        // 传统写法： 这样写会和数据库交互两次 ① 建立连接（从连接池获取连接）② 验证 ③ 发送sql   ④编译sql， sql解析语法树 ⑤ sql 语法验证   ⑥ 执行sql 查询 ⑦ 数据返回
        // 数据库优化原则：尽量减少和数据库直接交互！比如SQL 优化将两条SQL 汇总成一条 ，然后在业务中处理！ 使用缓存中间件比如 redis
//        List<TradeAmtVO<OptionVO>>  t_1List = marketIndexInfoMapper.selectTradeCount(t_1_open,t_1_close,ids);
//        List<TradeAmtVO<OptionVO>>  tList = marketIndexInfoMapper.selectTradeCount(t_open,t_close,ids);
        // T日和T-1数据都有~
        List<OptionVO> list = marketIndexInfoMapper.selectTradeCount(t_1_open, t_close, ids);
        // 业务处理！将 T-1 和 T日数据分离
        // 根据T 日时间 获取 T 日期
        LocalDate tDate = t_open.toLocalDate();
        // 根据T-1日时间   获取 T-1 日期
        LocalDate t_1Date = t_1_open.toLocalDate();
        // 过滤 T日数据
        List<OptionVO> tList = list.stream().filter((vo) -> {
            LocalDateTime parse = LocalDateTime.parse(vo.getTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            LocalDate targetDate = parse.toLocalDate();
            // 判断日期是否T日
            return targetDate.isEqual(tDate);
        }).toList();
        // 过滤t_1 日数据
        List<OptionVO> t_1List = list.stream().filter((vo) -> {
            LocalDateTime parse = LocalDateTime.parse(vo.getTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            LocalDate targetDate = parse.toLocalDate();
            // 判断日期是否T-1日
            return targetDate.isEqual(t_1Date);
        }).toList();
        // 3. 组装数据
        return TradeAmtVO.<OptionVO>builder()
                .volList(tList)
                .yesVolList(t_1List)
                .build();
    }

    @Override
    public UpDownVO<OptionVO> upDown() {
        // 股票业务： 开盘时间 9：30 -11：30   13：0-15：00
        // 时间条件如果没有特殊说明都指： 当前时间最近的交易日（如果当前是交易日就是当前时间，不是交易日 找最近的一个交易日）
        // 业务时间计算封装工具类中了 DateTimeUtil getLastDateTime4Stock 就是获取当前时间最近的交易日！
        LocalDateTime last = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());

        // TODO 模拟的开始时间  2022-01-06 09:25:00
        // 定义开始时间
        LocalDateTime begin = LocalDateTime.parse("2022-01-06 09:25:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        // TODO 模拟结束时间   2022-01-06 14:25:00
        // 结束时间
        LocalDateTime end = LocalDateTime.parse("2022-01-06 14:25:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        // 获取涨停列表
        List<OptionVO> upList = stockRtInfoMapper.selectUpOrDown(begin, end, true);
        // 获取跌停列表
        List<OptionVO> downList = stockRtInfoMapper.selectUpOrDown(begin, end, false);

        // 数据组装完毕
        return UpDownVO.<OptionVO>builder()
                .upList(upList)
                .downList(downList)
                .build();
    }

    @Override
    public PageResult<MoreVO> more(Integer page, Integer pageSize) {
        // 前提： 简单的逻辑可以在数据库层面实现，限定数据范围 （当前时间点） 数据库按分钟采集的数据，此时最多5000条）
        // 获取当前时间最近的普票交易时间
        LocalDateTime last1 = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        //TODO  mock data  2021-12-30 09:32:00
        final LocalDateTime last = LocalDateTime.parse("2021-12-30 09:32:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //TODO  mock data

        // 使用PageHelper 分页处理
        Page<MoreVO> result = PageHelper.startPage(page, pageSize).doSelectPage(() -> {
            List<MoreVO> voList = stockRtInfoMapper.selectMore(last);
        });

        // 组装数据
        return PageResult.<MoreVO>builder()
                .size(result.size())
                .totalRows(result.getTotal())
                .totalPages(result.getPages())
                .pageNum(result.getPageNum())
                .pageSize(result.getPageSize())
                .rows(result.getResult())
                .build();
    }

    @Override
    public List<IncreaseVO> stockIncrease() {

        // 1. 时间条件 此时此刻 精确分钟级别
        LocalDateTime last = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());

        // TODO mock data
        last = LocalDateTime.parse("2021-12-30 09:32:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        // TODO mock data
        // 从数据库中获取当前这一分钟所有的股票数据
        List<StockRtInfo> stockRtInfoList = stockRtInfoMapper.selectByDateTime(last);
        // 2. 对5000 个数据运算和排序
        // 结果
        List<IncreaseVO> result = new ArrayList<>();
        stockRtInfoList.stream().forEach((stock) -> {
            // 创建一个VO
            IncreaseVO vo = new IncreaseVO();
            // 向VO填充数据
            vo.setCode(stock.getStockCode()); // 代码
            vo.setName(stock.getStockName()); // 名称
            vo.setTradePrice(stock.getCurPrice()); //现价
            vo.setTradeAmt(stock.getTradeAmount()); //总手
            vo.setPreClosePrice(stock.getPreClosePrice()); // 前收盘价
            vo.setTradeVol(stock.getTradeVolume()); // 成交额
            vo.setCurDate(stock.getCurTime().toLocalDate().format(DateTimeFormatter.ofPattern("yyyyMMdd"))); // 当前日期

            // 计算三个量
            // ① 涨幅
            BigDecimal increase = (stock.getCurPrice().subtract(stock.getPreClosePrice())).divide(stock.getPreClosePrice(), new MathContext(6)).multiply(new BigDecimal(1.0));
            // ②  涨跌
            BigDecimal upDown = stock.getCurPrice().subtract(stock.getPreClosePrice());
            // ③  振幅
            BigDecimal range = (stock.getMaxPrice().subtract(stock.getMinPrice())).divide(stock.getPreClosePrice(), new MathContext(6)).multiply(new BigDecimal(1.0));
            vo.setIncrease(increase);
            vo.setUpDown(upDown);
            vo.setAmplitude(range);
            result.add(vo);
        });
        return result.stream().sorted((o1, o2) -> o2.getIncrease().compareTo(o1.getIncrease())).limit(10).toList();
    }

    @Override
    public List<SectorAllVO> sectorAll() {

        // 1. 时间条件（当前时间） 2、查询前10条 3、交易时间降序查询

        // 1. 获取离当前时间最近的一个交易日 （T+1）
        LocalDateTime last = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());

        // TODO mock data 模拟数据
        last = LocalDateTime.parse("2021-12-21 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        LocalDateTime begin = DateTimeUtil.getOpenDate(last);
        LocalDateTime end = DateTimeUtil.getCloseDate(last);
        // TODO mock data 模拟数据

        //2.  板块数据集合
        List<StockBlockRtInfo> blockRtInfoList = stockBlockRtInfoMapper.selectByDateTimeDesc(begin, end);

        // 3. 数据组装返回 KPI绩效考核
        return blockRtInfoList.stream().map((blockRtInfo) -> SectorAllVO.builder()
                .name(blockRtInfo.getBlockName())
                .code(blockRtInfo.getLabel())
                .avgPrice(blockRtInfo.getAvgPrice())
                .companyNum(blockRtInfo.getCompanyNum())
                .updownRate(blockRtInfo.getUpdownRate())
                .tradeVol(blockRtInfo.getTradeVolume())
                .tradeAmt(blockRtInfo.getTradeAmount())
                //TODO 2024-06-26 ->20240626
                .curDate(blockRtInfo.getCurTime().toLocalDate().format(DateTimeFormatter.ofPattern("yyyyMMdd")))
                .build()).toList();
    }

    @Override
    public List<MarketIndexVO> innerIndexAll() {
        // 1. 获取上证和深成指数大盘编码 （未来可能还会有其他大盘数据，因此不可以写死，需要定义配置文件）
        List<String> ids = marketIndexProperties.getInner();
        // 2. 指定当前时间 [股票交易时间：9：30-11：30 ，13：00-15：00  （除法定节假日外工作日）]
        // 获取距离当前时间最近的有效股票交易时间 比如 2024-06-26 11：20  有效时间 2024-06-26 11：20  ，2024-06-26 11：35  有效时间 2024-06-26 11：30
        LocalDateTime last = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        // TODO mock data  模拟数据 【一定要是用我提供的】
        String mockDate = "20211226105600";
        last = LocalDateTime.parse(mockDate, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        // TODO mock data
        // 3. 查询数据库
        // 3.1 根据大盘编码 和 指定时间 查询stock_market_index_info 表股票大盘数据详情表
        List<StockMarketIndexInfo> stockMarketIndexInfoList = marketIndexInfoMapper.selectByIdsAndTime(ids, last);
        // 3.2 根据大盘编码 和 指定日期 查询stock_market_log_price 表 大盘开盘价和收盘价流水表
        List<StockMarketLogPrice> stockMarketLogPriceList = marketLogPriceMapper.selectByIdsAndDate(ids, last.toLocalDate());
        // 4. 组装数据
        return stockMarketIndexInfoList.stream().map((info) -> {
            MarketIndexVO vo = MarketIndexVO.builder().build();
            vo.setCode(info.getMarkId());
            vo.setName(info.getMarkName());
            vo.setCurDate(info.getCurTime().toLocalDate().toString());
            vo.setTradeAmt(info.getTradeAccount());
            vo.setTradePrice(info.getCurrentPrice());
            vo.setTradeVol(info.getTradeVolume());
            vo.setUpDown(info.getUpdownRate());
            stockMarketLogPriceList.forEach((logPrice) -> {
                if (info.getMarkId().equals(logPrice.getMarketCode())) {
                    vo.setOpenPrice(logPrice.getOpenPrice());
                    vo.setPreClosePrice(logPrice.getPreClosePrice());
                }
            });
            return vo;
        }).toList();

    }

    @Override
    public List<StockBusiness> getStockBusiness() {

        return stockBusinessMapper.selectAll();
    }
}
